package event

import (
	"context"
	"errors"
	"reflect"
	"sync"

	"gitee.com/lihaiping1603/utils/log"
)

type Event string

var (
	// DefaultMaxListeners is the number of max listeners per event
	DefaultMaxListeners = 10
	DefaultMaxEvent     = DefaultMaxListeners
)

var (
	ErrorOverMaxListeners error = errors.New("the event listeners over max listeners limit")
)

var defaultEmmiter = NewEventEmit()

var (
	AddListener    = defaultEmmiter.AddListener
	RemoveListener = defaultEmmiter.RemoveListener
	Emit           = defaultEmmiter.Emit
	On             = defaultEmmiter.On
	Off            = defaultEmmiter.Off
)

type EventListener func(event Event, args ...interface{})

type Emitter interface {
	AddListener(event Event, listener EventListener) error
	RemoveListener(event Event, listener EventListener) error
	Emit(event Event, args ...interface{}) error
	On(event Event, listeners ...EventListener) error
	Off(event Event) error
}

type emitEvent struct {
	event Event
	Args  []interface{}
}

type EventEmit struct {
	cap       int
	mutex     sync.Mutex
	listeners map[Event][]EventListener
	eventCh   chan emitEvent
}

func NewEventEmit() Emitter {
	return &EventEmit{cap: DefaultMaxListeners,
		listeners: make(map[Event][]EventListener),
		eventCh:   make(chan emitEvent, DefaultMaxEvent)}
}

func (ee *EventEmit) On(event Event, listeners ...EventListener) error {
	listeners_len := len(listeners)
	if listeners_len > ee.cap {
		log.Error("listeners len:%d >Event cap:%d", listeners_len, ee.cap)
		return ErrorOverMaxListeners
	}
	ee.mutex.Lock()
	defer ee.mutex.Unlock()
	if _listeners, ok := ee.listeners[event]; ok {
		if len(_listeners)+listeners_len > ee.cap {
			log.Error("Event cap:%d over", ee.cap)
			return ErrorOverMaxListeners
		}
		ee.listeners[event] = append(_listeners, listeners...)
	} else {
		ee.listeners[event] = listeners
	}
	return nil
}

func (ee *EventEmit) Off(event Event) error {
	ee.mutex.Lock()
	defer ee.mutex.Unlock()
	delete(ee.listeners, event)
	return nil
}

func (ee *EventEmit) Emit(event Event, args ...interface{}) error {
	if ee.eventCh != nil {
		ee.eventCh <- emitEvent{event: event, Args: args}
	}
	return nil
}

func (ee *EventEmit) AddListener(event Event, listener EventListener) error {
	return ee.On(event, listener)
}

func (ee *EventEmit) RemoveListener(event Event, listener EventListener) error {
	ee.mutex.Lock()
	defer ee.mutex.Unlock()
	if _listeners, ok := ee.listeners[event]; !ok {
		return nil
	} else {
		listeners := []EventListener{}
		idx := -1
		listenerPointer := reflect.ValueOf(listener).Pointer()
		for index, _listener := range _listeners {
			_listenerPointer := reflect.ValueOf(_listener).Pointer()
			if _listenerPointer == listenerPointer {
				idx = index
				break
			}
		}
		if len(_listeners) > 1 {
			listeners = append(_listeners[:idx], _listeners[idx+1:]...)
		}
		ee.listeners[event] = listeners
	}
	return nil
}

func (ee *EventEmit) eventListeners(event Event) []EventListener {
	ee.mutex.Lock()
	defer ee.mutex.Unlock()
	resListeners := []EventListener{}
	if listeners, ok := ee.listeners[event]; ok {
		resListeners = append(resListeners, listeners...)
	}
	return resListeners
}

func (ee *EventEmit) cycle(ctx context.Context) {
	for {
		select {
		case emit_event := <-ee.eventCh:
			listeners := ee.eventListeners(emit_event.event)
			callback := func() {
				for _, listener := range listeners {
					listener(emit_event.event, emit_event.Args...)
				}
			}
			callback()
			//go callback()
		case <-ctx.Done():
			break
		}
	}
}
